home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / ODF Release 3 / Documentation / Engineering Notes / Other View Systems < prev    next >
Encoding:
Text File  |  1996-12-13  |  22.2 KB  |  384 lines  |  [TEXT/ttxt]

  1. OpenDoc
  2. Development
  3. Framework
  4.                                                                                                                                                                                                    
  5. Other View Systems
  6. ODF Release  3                                                                                                                                                                        
  7.  
  8. Table of Contents
  9.  
  10. • Introduction
  11. • Changes Since Release 2
  12. • Adding Constructor Support to Your Part
  13. • Runtime View Editing
  14. • Supported View Classes
  15. • Menu Support
  16.  
  17.  
  18. Introduction
  19.  
  20. The “Other View Systems” folder in ODF is an add-on to support the MetroWerks Constructor view editor. It enables your part to read the PowerPlant view format and create equivalent ODF views, so you can build your user interface using a visual tool.
  21.  
  22. There are two main benefits to using Constructor to do view editing:
  23.  
  24. • Faster User Interface Development
  25. A view editor enables you to experiment with view layouts much more easily than by editing text-based descriptions (the standard ODF approach). Also, because there is no intermediate compiling step, views can be edited and reloaded while the part is still running.
  26.  
  27. • Code Migration
  28. This provides an extra step in moving from applications to parts. You can now move PowerPlant-based application code to PowerPart, and from there to ODF to gain cross-platform capability without having to change the user interface very much. The application code itself must still be converted manually, however being able to reuse the same views does help.
  29.  
  30. The add-on works with the current ODF 1 release.  No changes to the framework classes were necessary, all the code doing the resource parsing is isolated in a few files.
  31.  
  32. The main idea is to parse the resource stream the same way the PowerPlant code does.  For each view, the data is read in and converted as necessary (for example, integers to fixed point, text trait handles to ink objects). Then an equivalent ODF view object is created.
  33.  
  34. Using this add-on in any part is only a matter of adding the Constructor View Parser support files and overriding a single method to call the support code instead of the ODFRC view reading code. If you use custom views you will need to add code to create them as well.
  35.  
  36. There are two main limitations to the Constructor support:
  37.  
  38. • The ODF view system only supports one scroller per frame; only the “content” view can scroll. There are ways to handle additional scrollers for situations like secondary text fields (see ODFForm for an example).
  39.  
  40. • ODF doesn’t have as extensive a set of view classes as does Constructor. The gridview class, for instance, isn’t yet official and isn’t supported by this add-on.
  41.  
  42.  
  43. Changes Since Release 2
  44.  
  45. More view classes have been added since R2. There is a new FW_CGraphicControl for artwork and graphic controls. A new FW_CIncludeView can include views from other resources. And the PPob reader has been rewritten since ODF R2, making custom view types much easier to support.
  46.  
  47. FW_CGraphicControl
  48.  
  49. Many view systems have a large number of classes to deal with “artwork,” like icons and picture buttons. ODF now has a single view class, FW_CGraphicControl, which handles this job. It uses shape objects, part of ODF’s cross-platform object-oriented graphics engine, to represent artwork graphics. FW_CGraphicControl can have up to 4 shape objects to represent different states: normal, “on”, pushed-normal and pushed-”on”.
  50.  
  51. FW_CGraphicControl is created automatically for you when the PPob reader encounters LTextButton, LButton, LToggleButton, or LIconPane. LCicnButton is not supported because the ODF graphics engine only supports ‘icxx’ icon families, not the older ‘cicn’ color icon type.
  52.  
  53. ODFForm’s content view contains two graphic control views (the OpenDoc™ and ODF icons).
  54.  
  55. FW_CIncludeView
  56.  
  57. FW_CIncludeView is a “placeholder” view - it is placed in a view hierarchy, and when loaded will include a second view hierachy from a different resource. This should greatly simplify the creation of root frame, embedded frame, and printing frame hierarchies. Now you can have three “root” hierarchies with various combinations of scrollbars and rulers, and have all three include the main content views from a fourth, independent resource. See ODFForm’s PPView.ppob for an example.
  58.  
  59. To use FW_CIncludeView in Constructor, you need to copy the custom pane type definition from FWPPObs.rsrc, which is in the “PowerPlant PPob” folder (in the “Other View Systems” folder in ODF).
  60.  
  61. Here’s how you would go about laying out your view hierarchies.
  62.  
  63. Root Frame: Create a new view resource in Contstructor and use a window at the top. Put a scroller in it (if you need scrolling or a content view). Then put an FW_CIncludeView inside the scroller.  Make sure to give the scroller the ID# of your content view (ODFForm uses 100), and give the include view the ID of your content view resource (ODFForm uses 1000).
  64.  
  65. Content view: Create this without  a window at the top. ODFForm uses a CFormView (LPicture subclass) at the top. You can do this by dragging your view off of the view palette into the catalog window. You can also use the New Window Resource menu item and choose LView from the view type popup if you don’t need a special subclass. Make sure the view hierarchy has the correct resource ID (e.g. 1000 in ODFForm), and if you are using a content view, give it your content ID# (e.g. 100 in ODFForm).
  66.  
  67. Getting views aligned properly can be a challenge. Make sure your include view and included view have the same dimensions.
  68.  
  69. Custom View Types (Enhancing the PPob Reader)
  70.  
  71. Custom pane types used to require defining a subclass and several methods. Now you only need to write one method. Here are two examples from ODFForm, a password view and a content view.
  72.  
  73. • Password Text View
  74.  
  75. ODFForm has a subclass of FW_CEditView called CPwdEditView. In Constructor, this view is defined by using an LEditField with a class ID of ‘PwEd’. It doesn’t have any extra fields.
  76.  
  77. The view class is registered in CFormPart::Initialize:
  78.     FW_CPPobReader::RegisterClass('PwEd', ReadPasswordEditView);
  79.  
  80. and defined in Dialog.cpp:
  81.  
  82. void ReadPasswordEditView (Environment* ev,
  83.     FW_CPPobReader* context, FW_CReadableStream& stream)
  84. {
  85.     // Read data for LEditView
  86.     FW_SPPobEditInfo info (stream);
  87.     
  88.     // Create a CPwdEditView
  89.     CPwdEditView* view = FW_NEW (CPwdEditView, (ev, context->GetSuperView(),
  90.         info.fBounds, info.fViewID));
  91.     view->SetBindings (ev, info.fBindings);
  92. }
  93.  
  94. • Content View
  95.  
  96. ODFForm’s content view, CFormView, is a subclass of FW_CPictSView. It adds a field for a second picture resource ID. In Constructor, ODFForm has a custom pane type definition which inherits from LPicture and has a class ID of ‘Frmv’.
  97.  
  98. The view class is registered in CFormPart::Initialize:
  99.     FW_CPPobReader::RegisterClass('Frmv', ReadFormView);
  100.  
  101. and defined in View.cpp:
  102.  
  103. void ReadFormView (Environment* ev,
  104.     FW_CPPobReader* context, FW_CReadableStream& stream)
  105. {
  106.     // Read the basic LPicture data. FWPPobOb.h doesn't define an LPicture
  107.     // struct; we'll use the superclass, LView.
  108.     
  109.     // Read the LView data
  110.     FW_SPPobViewInfo info (stream);
  111.     
  112.     // Read the LPicture data
  113.     FW_ResourceID pict1;
  114.     stream >> pict1;
  115.     
  116.     // Now read our CFormView data
  117.     FW_ResourceID pict2;
  118.     stream >> pict2;
  119.     
  120.     CFormView* view = FW_NEW (CFormView, (ev, context->GetSuperView(),
  121.         info.fBounds, pict1, pict2));
  122.     view->SetBindings (ev, info.fBindings);
  123.     
  124.     // CFormView is capable of having subviews, so tell the PPob reader
  125.     context->SetNextSuperView (view);
  126. }
  127.  
  128. PowerPlant views are archived in much the same way as ODF views, so reading them in is easy.  In this case, LPicture is a subclass of LView. FWPPobOb.h and FWPPobOb.cpp contain a utility class for reading in LView data, but not for LPicture. Since the LPicture data is just one single resource ID after the inherited LView data, we can use the LView utility class (FW_SPPobViewInfo) and then read the resource ID. After that, we defined CFormView to have one resource ID, which we also read.
  129.  
  130. The final step after creating the view is to deal with subviews. Any view which might have subviews must call the PPob reader’s SetNextSuperView to set things up. If you forget, any subviews will be placed inside the current superview (the parent of this view) and will be located relative to its upper left corner.
  131.  
  132. Adding Constructor Support to Your Part
  133.  
  134. We assume here that you have already started to build your part’s user interface with Constructor, or that you are re-using existing view resources.  This section explains how to put things together in your project and modify your source code in order to use these views.  All code samples are taken from ODFForm.
  135.  
  136. Follow these steps:
  137.  
  138. 1) Add the view resource file to your project
  139.  
  140. The view reloading code (see below) assumes the file is called “PPViews.ppob”. If you don’t care about live view reloading, you can give the file any name you wish.
  141.  
  142. 2) Add the PPob reader source and other support files to your project 
  143.  
  144. You will need to add FWPPobRd.cpp and FWPPobOb.cpp to your project. These are located in the “ODF : Other View Systems : PowerPlant PPob” folder. You also need FWScrlVw.cpp if you wish to use LScroller (you probably do), and FWPPobMn.cpp and FWMacMnu.cpp if you want to use Constructor to edit Mac OS-format menu resources.
  145.  
  146. 3) Add the class registrations in your part's Initialize() method
  147.  
  148.     FW_CPPobReader::RegisterAllPPClasses();
  149.  
  150. Custom view types require separate registration as described above. For example:
  151.     FW_CPPobReader::RegisterClass('Frmv', ReadFormView);
  152.  
  153. 4) Pass 0 for the view resource IDs in your call to RegisterPresentation.
  154.  
  155. Check that your part’s call to RegisterPresentation is not passing a view id for root or embedded frames. Doing so would make ODF try to load the native ODFRC resource with that id instead of calling the frame’s CreateSubViews method, which is what we need.
  156.  
  157. For example you should have something like:
  158.     fMainPresentation = RegisterPresentation(ev, kMainPresentation, TRUE);
  159. instead of
  160.     fMainPresentation = RegisterPresentation(ev, kMainPresentation, TRUE, 
  161.         kEmbeddedFormView, kRootFormView);
  162.  
  163. 5) Implement your frame's CreateSubViews method
  164.  
  165. This is where you tell ODF to use the PPob Reader module instead of loading native ODF resources.
  166.  
  167. void CFormFrame::CreateSubViews(Environment* ev)
  168. {        
  169.     FW_CPPobReader::CreateViews (ev, 
  170.         kFormView,     // 'PPob' view id
  171.         this,          // frame is the root view
  172.         this,          // frame is receiver for all controls
  173.     ); 
  174. }
  175.  
  176. The 3rd parameter is "this" in order to make the frame the root view of the views being loaded (it could be another superview if one already existed.)
  177.  
  178. The 4th parameter is usually"this" too in order to make the frame the receiver for all controls being loaded.  This avoids having to make an extra call to FW_CPPobReader::LinkReceiverToControls after loading the views.  Use a NULL argument if you don't want to link controls automatically.  You can also call LinkReceiverToControls afterward and pass a different receiver.
  179.  
  180. CreateViews may throw an exception (for a missing icon resource, for example). The debug version will catch these and warn you.
  181.  
  182. 6) Add code to support custom classes, including your frame's content view.
  183.  
  184. When your frame uses a separate content view (like CFormView in Form) or when it needs to use other custom view classes (like CScrollEdit in Form) you must define the mapping between these ODF classes and the PowerPlant classes used in the part's UI.  Basically you need a little extra code to extend the PPob reader module in order to have the right conversion done at load time.  See the discussion on custom views in the section on Form above.
  185.  
  186. IMPORTANT: Remember that an ODF frame can use only one scrolling view for its content (the content view).  If your UI doesn't contain any scrollers the frame remains the content view.  Otherwise the first scrolling view is converted into the content view and additional scrollers will generate a warning and be ignored by ODF.  However there are ways to handle standard scrolling views such as text-edit and list boxes.  See how this is done in Form.
  187.  
  188. 7) Add the "Reload Views" menu command if necessary
  189.  
  190. Having a "Reload Views" command is useful in debug mode while you are developing your part and adjusting its interface.  See how this is implemented in Form.
  191.  
  192. The ReloadViews method takes the same arguments as CreateViews:
  193.  
  194. FW_PlatformError error = FW_CPPobReader::ReloadViews (ev, 
  195.     kFormView, 
  196.     formFrame, 
  197.     formFrame);
  198.  
  199. This command opens the “:Sources:PPViews.ppob” file in the same location as the part; you can also look at the implementation of ReloadViews and write a similar method (it’s small).
  200.  
  201. WARNING:  ReloadViews starts by calling FW_CFrame::DeleteAndResetViews to delete all the frame's subviews and then it calls CreateViews to load the views again.  In general your part shouldn't be affected by this operation, i.e. it should be the same as if the views were created for the first time when the frame was created.  However the destruction of views may have side effects in your code that you will have to deal with.
  202.  
  203. 8) Build and test
  204.  
  205. The main sources of problems will be one of the following:
  206.  
  207. • Your part launches and then quits again.
  208. Probably an exception was thrown during view creation. Make sure to compile a Debug version of the project to work out all errors.
  209.  
  210. • Your part launches but crashes when the frame is created.
  211. Probably you under- or over-read data from the stream. Step through all of your custom view type functions to make sure you are reading all the PowerPlant fields correctly.
  212.  
  213. Additional Notes
  214.  
  215. • It is possible to use more than one view resource kind at a time. You could use ODFRC views for simple frames and only use PPob views for your content frame.
  216.  
  217. • Using #define flags in order to switch between view systems was done for the sake of the ODFForm example and doesn't reflect normal cases where only one kind of view is used.
  218.  
  219. • In this release it is not possible to save the converted views back into ODF format in order to avoid linking with the PowerPlant reader code.  This may be added in the future.
  220.  
  221. • PICT resources (for Picture views) and MENU resources (for popup menus) must be present in the current resource file.
  222.  
  223. Runtime View Editing
  224.  
  225. It is very convenient to have "live" view editing - that is, to be able to edit your view hierarchy in the view editor and see then changes in your part without having to recompile it. The FW_CPPobReader class has utility methods to support this.
  226.  
  227. To use it:
  228.  
  229. • Name your view resource file “PPViews.ppob” (sorry, this is hard-coded into the PPob reader).
  230.  
  231. • Add a menu item to your part called "Reload Views". In your menu handling code in your frame call FW_CPPobReader::RecreateViews (ev, viewID, superview, receiver). For example:
  232.  
  233. Menus.fr:
  234.     FW_RTextItem (cReloadPPobViews, 'R', "Reload Views");
  235.  
  236. Frame.cpp:
  237.     FW_Handled MyPart::DoMenu (Environment* ev, 
  238.         const FW_CMenuEvent& theMenuEvent)
  239. {
  240.     if (theMenuEvent.GetCommandID(ev) == cReloadPPobViews)
  241.         FW_CPPobReader::RecreateViews (ev, kRootFormView, formFrame,
  242.             formFrame);
  243.     ....
  244.  
  245. You should of course still include your views resource file in the project. These will be used whenever the part creates a frame (i.e. whenever you open your document), so you will only get the updated views after calling the RecreateViews method, or relinking your part.
  246.  
  247. The "Reload Views" command will be modified in the next release so that it can find the <part>.rsrc or <part>.ppob resource file by default in the Sources folder where it generally resides.
  248.  
  249. The "Reload Views" command doesn’t restore the scrollbars properly. The effect is only cosmetic; they still work. The problem won’t be present for normally-loaded views.
  250.  
  251.                                                                                                                                                                                   
  252. Supported View Classes
  253.  
  254. The following class conversion occurs when reading a PPob resource:
  255.  
  256. Constructor             ODF class
  257. LPane                   (abstract)
  258. LView                   FW_CSuperView
  259. LScroller               FW_CScrollBarScroller
  260. LActiveScroller         FW_CScrollBarScroller
  261. LTextEdit               FW_CEditView
  262. LPicture                FW_CPictSView
  263. LWindow                 (sets frame size & creates grow box)
  264. LDialogBox              (sets frame size & default/cancel buttons)
  265. LControl                (abstract)
  266. LStdControl             FW_CScrollbar
  267. LStdButton              FW_CButton
  268. LStdCheckBox            FW_CButton
  269. LStdRadioButton         FW_CButton
  270. LStdPopupMenu           FW_CPopupMenu
  271. LTextButton             FW_CGraphicControl
  272. LButton                 FW_CGraphicControl
  273. LCicnButton             (not supported)
  274. LToggleButton           FW_CGraphicControl
  275. LIconPane               FW_CGraphicControl
  276. LCaption                FW_CStaticText
  277. LGroupBox               FW_CGroupBox
  278. LEditField              FW_CEditView
  279. LListBox                FW_CListBox
  280. LTabGroup               FW_CViewTabber
  281. LRadioGroup             FW_CRadioCluster
  282.  
  283. Any unknown classes will result in dropping into the debugger. To support custom classes, see the “Changes Since Release 2:Custom View Types” section on writing and registering custom view type functions.
  284.  
  285. Conversion Rules and Limitations
  286.  
  287. ODF views are restricted to 16-bit coordinates. When reading coordinates or  sizes greater than 16 bits the PPob reader gives a debugger warning.
  288.  
  289. LAttachment classes in the stream are not supported.
  290.  
  291. LPane classes have the following conversion rules and limitations:
  292. (class fields are named as they appear in the Constructor's class editors)
  293.  
  294. • LPane fields:
  295. Binding flags and Pane ID are converted.
  296. User Constant field, Enabled and Visible flags are ignored.
  297.  
  298. • LView fields:
  299. Scroll Unit and Scroll Position are ignored. 
  300. Image Size is ignored. Only the content view in ODF has an extent different than its physical size.
  301. When an LView object is a scroller's scrolling view its bounds are changed to match the scroller's bounds (minus the scroll bars) and its binding flags are set the the scroller's binding flags.
  302.  
  303. • LScroller fields:
  304. An ODF frame can use only one scrolling view for its content (the content view).  The first scrolling view is converted into the content view and additional scrollers generate a warning and are ignored by ODF. (However you can handle scrolling views as custom views like CScrollEdit in Form.)
  305. Also you must  use an LScroller to specify which view is to be the content view. This can be a problem for printing frames, which don’t need scrollbars, so see ODFForm, which has a scroller in its printing frame but places it so the scrollbars aren’t visible. 
  306. The LScroller object itself is converted into an ODF FW_CScrollerView and 1 or 2 scrollbars are created. Their view ids are hard-coded to 'horz' and 'vert'.
  307.  
  308. • LStdControl fields:
  309. Title, Value Message, Initial Value and Text Traits are converted. Control Ref Con is ignored.
  310. IMPORTANT: the Value Message should be the ODF value. For instance a button should use -10 (FW_kButtonPressedMsg) if you want to use the default ODF notification. Of course you can also use your own message values.
  311.  
  312. • LWindow fields:
  313. The only data converted from an LWindow object is its location and the presence of a grow box. (A grow box is always created in the bottom right corner of the frame and its view id is hard-coded to 'grow'.) The type of window cannot be used here because the frame's window already exists when its subviews are created.
  314.  
  315. • LDialogBox fields:
  316. Same as LWindow + the default and cancel button ids.  The type of dialog window must be set by program in NewModalDialog().
  317.  
  318. • LListBox fields:
  319. Horizontal Scrollbar and LDEF ID are not supported. Single selection is the default.
  320.  
  321. • LEditField fields:
  322. Key filter is not supported.
  323.  
  324. • LTextEdit fields:
  325. This class is mapped to the same ODF FW_CEditView class as LEditField, because ODF doesn't have any built-in scrolling text class yet. However, ODFForm provides an example custom CScrollEdit class.
  326.  
  327. • LPicture fields:
  328. The image size should remain 0, 0 in order to use the picture size by default.
  329. The PICT resource must be present in the same resource file.
  330.  
  331.  
  332. Menu Support
  333.  
  334. ODF contains support for menu and menu bar resources edited in MetroWerks Constructor. These are the standard Mac OS resource formats, plus a side table for the command numbers. There are four files that provide support for menus:
  335.     FWPPobMn.h, FWPPobMn.cpp
  336.     FWMacMnu.h, FWMacMnu.cpp
  337.  
  338. FWPPobMn.h declares two functions that you can call in your part to create menus from PowerPlant menu resources. These functions are described below.
  339.  
  340. FWMacMnu.h and FWMacMnu.cpp contain helper classes that read and store data from the resource types 'MBAR', 'MENU', and 'Mcmd'. The 'MBAR' resource contains a list of menu resource IDs. The 'Mcmd' resource type contains a list of command numbers for a single menu.
  341.  
  342.  
  343. Initializing a Menubar
  344.  
  345. void FW_MacInitMenuBarFromResource(Environment* ev, 
  346.     FW_ResourceID menuBarResourceID,
  347.     FW_CMenuBar* menuBar);
  348.  
  349. This function reads a Macintosh menubar resource, then reads the menu resources listed in that resource. The resource data is converted into ODF menus and added to the specified menu bar. 
  350.  
  351. If a menu resource is an Apple menu, it is not added to the menu bar. Instead, the first item string in the Apple menu is used to set the command string for the OpenDoc About command.
  352.  
  353. The best place to call this function is in an override of FW_CPart::InstallMenus. See the sample code below.
  354.  
  355.  
  356. Creating a Menu
  357.  
  358. FW_CPullDownMenu* FW_MacCreateMenuFromResource(Environment* ev, 
  359.     FW_ResourceID menuResourceID);
  360.  
  361. This function reads a menu resource and uses it to create an ODF pulldown menu. You can then add this menu to your menu bar if desired.
  362.  
  363.  
  364. Sample Code
  365.  
  366. void CMyPart::InstallMenus(Environment* ev, FW_CMenuBar* menuBar)
  367. {
  368.     // Create menus from PowerPlant resources
  369.     FW_MacInitMenuBarFromResource(ev, kMenuBarResourceID, menuBar);
  370.  
  371.     // Set the About item string, if available
  372.     FW_CString aboutString;
  373.     menuBar->GetItemString(ev, kODCommandAbout, aboutString);
  374.     if (!aboutString.IsEmpty())
  375.         this->SetAboutString(aboutString);
  376.  
  377.     // Create a single pulldown menu from a PowerPlant resource
  378.     FW_CPullDownMenu* pdMenu = FW_MacCreateMenuFromResource(ev,
  379.         kMenuResourceID);
  380.     if (pdMenu)
  381.         menuBar->AdoptMenuLast(ev, pdMenu);
  382. }
  383.  
  384.